/* eslint-disable @typescript-eslint/no-explicit-any */
import { cls } from '@knno/jsx';
import { selectTextEnd } from '../../editor';
import { PopList, type PopListSelectHandler } from '../../popList/popList';
import type { PopupPosition } from '../../popup/popup';
import { DATA } from '../../symbol';
import { cellStyle } from '../cell.css';
import { inlineStyle } from '../inline.css';
import { comboOptionStyle } from './combo.css';
import { TextInputBase, type TextInputProps } from './text';

const ASCII_LOWERCASE = Array.from('abcdefghijklmnopqrstuvwxyz');
const ASCII_UPPERCASE = ASCII_LOWERCASE.map((a) => a.toUpperCase());
const NUMBERS = Array.from('0123456789');
const KEYS = new Set<string>(
	['Backspace', 'Delete', 'Enter', ' ', 'Space'].concat(NUMBERS, ASCII_UPPERCASE, ASCII_LOWERCASE),
);

export type OptionValue = {
	value: unknown;
	text: string;
	desc?: string;
};

export type ComboValue = string | OptionValue;

export type ComboInputProps<T extends ComboInputBase<any, any, any>, ComboValue> = TextInputProps<
	T,
	ComboValue
> & {
	queryHandler?: (keyword: string) => Promise<ComboValue[]>;
	multiline?: undefined;
	popupWidth?: string | number;
	popupPosition?: PopupPosition;
};

abstract class ComboInputBase<
	P extends ComboInputProps<C, ComboValue>,
	C extends ComboInputBase<P, C, E>,
	E extends HTMLElement,
> extends TextInputBase<P, C, E, ComboValue> {
	private [DATA]?: PopListSelectHandler;
	private _noSelectAll = false;
	constructor(props: P) {
		super(props);
		if (typeof this.value === 'object' && this.value) {
			this.el.textContent = this.value.text;
		}
		let openTimer: number | undefined;
		this.el.addEventListener('keyup', (evt) => {
			if (!KEYS.has(evt.key) || evt.ctrlKey || evt.altKey || evt.metaKey) {
				return;
			}
			if (!this.readOnly) {
				if (openTimer) clearTimeout(openTimer);
				openTimer = setTimeout(() => {
					this.query();
				}, 100);
			}
		});
		this.el.addEventListener('keydown', (evt) => {
			if (evt.key === 'ArrowDown') {
				if (this[DATA]) {
					this[DATA].list.focus();
					this[DATA].list.setActive(0);
					evt.preventDefault();
				}
			} else if (evt.key === 'Escape') {
				if (this[DATA]) {
					this[DATA].close();
					evt.preventDefault();
				}
			}
		});
		// this.el.addEventListener('input', () => {
		// 	if (!this.el.textContent) {
		// 		this.updateValue(undefined);
		// 	} else {
		// 		this.updateValue(this.el.textContent);
		// 	}
		// });
	}
	private query() {
		const keyword = this.el.textContent;
		if (this.props.queryHandler && keyword) {
			this.props.queryHandler(keyword).then((data) => {
				if (
					this.el.textContent !== keyword ||
					(document.activeElement !== this.el && !this[DATA])
				) {
					return;
				}
				if (data.length === 0) {
					this[DATA]?.close();
					return;
				}
				if (!this[DATA]) {
					this.el.classList.add('popup-open');
					this[DATA] = PopList.show(data, {
						refer: this.el,
						popupPosition: this.popupPosition ?? 'bottom-right',
						width: this.popupWidth,
						focus: false,
						columnOption: {
							formatter(row) {
								const r = row as OptionValue;
								return (
									<div class={comboOptionStyle}>
										<div class="name">{r.text}</div>
										{r.desc ? <div class="desc">{r.desc}</div> : ''}
									</div>
								);
							},
						},
					})
						.onClose(() => {
							this.el.classList.remove('popup-open');
							this[DATA] = undefined;
						})
						.onSelect((value, text) => {
							this.el.classList.remove('popup-open');
							this.el.textContent = text;
							this.updateValue({ value, text });
							this._noSelectAll = true;
							selectTextEnd(this.el);
							setTimeout(() => {
								this._noSelectAll = false;
							});
						});
				} else {
					this[DATA].list.setRows(data);
				}
			});
		} else {
			this[DATA]?.close();
		}
	}
	protected override get noSelectAll(): boolean {
		return this._noSelectAll;
	}
	override setMultiline(_: boolean): this {
		return super.setMultiline(false);
	}
	get popupPosition(): PopupPosition | undefined {
		return this.props.popupPosition;
	}
	setPopupPosition(value: PopupPosition | undefined): this {
		this.props.popupPosition = value;
		return this;
	}
	get popupWidth(): string | number | undefined {
		return this.props.popupWidth;
	}
	setPopupWidth(value: string | number | undefined): this {
		this.props.popupWidth = value;
		return this;
	}
}

export type ComboCellProps = ComboInputProps<ComboCell, ComboValue> & {
	colSpan?: number;
	rowSpan?: number;
};

export class ComboCell extends ComboInputBase<ComboCellProps, ComboCell, HTMLTableCellElement> {
	protected render(): Node {
		return (
			<td
				class={cls(this.props.class, cellStyle)}
				style={this.props.style}
				colSpan={this.colSpan}
				rowSpan={this.rowSpan}
			/>
		);
	}
	get colSpan(): number {
		return this.props.colSpan ?? 1;
	}

	setColSpan(value: number): this {
		this.props.colSpan = value;
		this.el.colSpan = value;
		return this;
	}

	get rowSpan(): number {
		return this.props.rowSpan ?? 1;
	}

	setRowSpan(value: number): this {
		this.props.rowSpan = value;
		this.el.rowSpan = value;
		return this;
	}
}

export class ComboInline extends ComboInputBase<
	ComboInputProps<ComboInline, ComboValue>,
	ComboInline,
	HTMLSpanElement
> {
	protected render(): Node {
		return <span class={cls(this.props.class, inlineStyle)} style={this.props.style} />;
	}
}
